﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Globalization;
using System.IO;
using System.ComponentModel;
using MonoStrategy.RenderSystem;

namespace MonoStrategy
{
    public class RuntimeBuildingConfig : BuildingConfig
    {
        public RuntimeBuildingEditor Building { get; private set; }

        public RuntimeBuildingConfig(RuntimeBuildingEditor inBuilding)
        {
            Building = inBuilding;
        }
    }

    public class RuntimeBuildingEditor
    {
        public List<RenderableAnimationClass> ResourceStacks { get; private set; }
        public List<RenderableAnimationClass> SurroundingBuildings { get; private set; }
        public List<RenderableAnimationClass> SurroundingSettlers { get; private set; }
        public List<Point> GroundPlane { get; private set; }
        public List<Point> ReservedPlane { get; private set; }
        public Double ZMargin { get; private set; }
        public AnimationClass Class { get; private set; }
        public RenderableAnimationClass Renderable { get; private set; }
        public Point Position { get; private set; }
        public TerrainDefinition Terrain { get; private set; }
        public BuildingConfig Config { get; private set; }
        public bool ShowBuildings { get; set; }
        public bool ShowSettlers { get; set; }

        public void SetPosition(Point inPosition)
        {
            Point oldPosition = Renderable.Position.ToPoint();
            int xDelta =  - oldPosition.X + inPosition.X;
            int yDelta =  - oldPosition.Y + inPosition.Y;

            // clamp position to boundaries
            foreach (var stack in ResourceStacks)
            {
                if (stack.Position.XGrid + xDelta < 0)
                    xDelta = -stack.Position.XGrid;

                if (stack.Position.YGrid + yDelta < 0)
                    yDelta = -stack.Position.YGrid;
            }

            Position = new Point(oldPosition.X + xDelta, oldPosition.Y + yDelta);
            Renderable.Position = CyclePoint.FromGrid(Position);

            foreach (var stack in ResourceStacks)
            {
                stack.Position = CyclePoint.FromGrid(
                    stack.Position.XGrid + xDelta,
                    stack.Position.YGrid + yDelta);
            }
        }

        public RuntimeBuildingEditor(RenderableAnimationClass inRenderable, TerrainDefinition inTerrain)
        {
            Class = inRenderable.Class;
            Renderable = inRenderable;
            ReservedPlane = new List<Point>();
            ResourceStacks = new List<RenderableAnimationClass>();
            SurroundingBuildings = new List<RenderableAnimationClass>();
            SurroundingSettlers = new List<RenderableAnimationClass>();
            GroundPlane = new List<Point>();
            Terrain = inTerrain;
            Config = new RuntimeBuildingConfig(this);

            foreach (var rect in Class.GroundPlane)
            {
                for (int y = 0; y < rect.Height; y++)
                {
                    for (int x = 0; x < rect.Width; x++)
                    {
                        GroundPlane.Add(new Point(x + rect.X, y + rect.Y));
                    }
                }
            }

            foreach (var rect in Class.ReservedPlane)
            {
                for (int y = 0; y < rect.Height; y++)
                {
                    for (int x = 0; x < rect.Width; x++)
                    {
                        ReservedPlane.Add(new Point(x + rect.X, y + rect.Y));
                    }
                }
            }
        }

        public IEnumerable<Point> GetSurroundingSettlersSpots()
        {
            return GetSurroundingSpots(pos => GroundPlane.Contains(pos));
        }

        public IEnumerable<Point> GetSurroundingBuildingsSpots()
        {
            return GetSurroundingSpots(pos => Terrain.CanBuildingBePlacedAt(pos.X, pos.Y, Config));
        }

        private IEnumerable<Point> GetSurroundingSpots(Func<Point, bool> inHandler)
        {
            List<Point> spots = new List<Point>();
            int minX = (GroundPlane.Count > 0)?GroundPlane.Min(e => e.X):0;
            int minY = (GroundPlane.Count > 0)?GroundPlane.Min(e => e.Y):0;
            int[,] grid = new int[Terrain.Size, Terrain.Size];

            // set all grid cells occupied by a ground cell to "1"
            for (int y = 0; y < grid.GetLength(1); y++)
            {
                for (int x = 0; x < grid.GetLength(0); x++)
                {
                    Point pos = new Point(x + minX - 1, y + minY - 1);

                    if (inHandler(pos))
                        grid[x, y] = 1;
                }
            }

            // add all grid cells to spots that have neighbors of value "1"
            for (int y = 0, yCount = grid.GetLength(1); y < yCount; y++)
            {
                for (int x = 0, xCount = grid.GetLength(0); x < xCount; x++)
                {
                    Boolean hasNeighbour = false;

                    if (grid[x, y] == 1)
                        continue;

                    for (int iy = -1; iy < 2; iy++)
                    {
                        for (int ix = -1; ix < 2; ix++)
                        {
                            int posX = x + ix, posY = y + iy;

                            if ((posX < 0) || (posX >= xCount) || (posY < 0) || (posY >= yCount))
                                continue;

                            if (grid[posX, posY] == 1)
                                hasNeighbour = true;
                        }
                    }

                    if (hasNeighbour)
                        spots.Add(new Point(minX - 1 + x, minY - 1 + y));
                }
            }

            return spots;
        }

        public void Save()
        {
            Class.GroundPlane.Clear();
            Class.ReservedPlane.Clear();
            Class.ResourceStacks.Clear();

            foreach (var pos in GroundPlane)
            {
                Class.GroundPlane.Add(new Rectangle(pos.X, pos.Y, 1, 1));
            }

            foreach (var pos in ReservedPlane)
            {
                Class.ReservedPlane.Add(new Rectangle(pos.X, pos.Y, 1, 1));
            }

            foreach (var stack in ResourceStacks)
            {
                Class.ResourceStacks.Add(new ResourceStackEntry()
                {
                    Position = new Point(stack.Position.XGrid - Position.X, stack.Position.YGrid - Position.Y),
                    Resource = (Resource)Enum.Parse(typeof(Resource), stack.PlayingSets.First().Name),
                });
            }
        }
    }
}
